package icontext

/*

#cgo CFLAGS: -I../../../src_c -I../../../src_c/web_svc/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "appconfig.h"
#include "web_server.h"
#include "rrd.h"
#include "log.h"

*/
import "C"
import (
	"encoding/json"
	"fmt"
	"nd-app/pkg/factory"
	"nd-app/pkg/logger"
	"net"
)

type RadioContext struct {
	UdpConn *net.UDPConn
	Stats   RadioStats
}

type RadioStats struct {
	Freq          int64
	FreqSkew      float64
	RxBytes       uint64
	TxBytes       uint64
	RxNum         uint64
	TxNum         uint64
	SigLevel      float64
	SigLevelNum   uint32
	NoiseLevel    float64
	NoiseLevelNum uint32
}

type RadioMessage struct {
	Vdl2Phy struct {
		Freq                 int64   `json:"freq"`
		BurstLenOctets       int     `json:"burst_len_octets"`
		HdrBitsFixed         int     `json:"hdr_bits_fixed"`
		OctetsCorrectedByFec int     `json:"octets_corrected_by_fec"`
		Idx                  int     `json:"idx"`
		SigLevel             float64 `json:"sig_level"`
		NoiseLevel           float64 `json:"noise_level"`
		FreqSkew             float64 `json:"freq_skew"`
	} `json:"vdl2_phy"`
}

var radioContext *RadioContext

func InitRadio() error {
	var err error
	radioContext = &RadioContext{}

	s, err := net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%d", factory.AppConfig.Configuration.Ip, factory.AppConfig.Configuration.Port))
	if err != nil {
		logger.AppLog.Error("fail resolve udp addr:", err)
		return err
	}

	radioContext.UdpConn, err = net.ListenUDP("udp4", s)
	if err != nil {
		logger.AppLog.Error("fail listen udp:", err)
		return err
	}
	go udpServer()

	return nil
}

func udpServer() {
	defer func() {
		err := recover()
		if err != nil {
			logger.AppLog.Error("udp server panic:", err)
		}
	}()

	var message RadioMessage
	buffer := make([]byte, 16*1024)
	for {
		n, addr, err := radioContext.UdpConn.ReadFromUDP(buffer)
		if err != nil {
			logger.AppLog.Error("fail read from udp:", err)
			continue
		}
		logger.AppLog.Infof("receive message from %s, length: %d, message: %s", addr.String(), n, string(buffer[:n]))

		err = json.Unmarshal(buffer[:n], &message)
		if err != nil {
			logger.AppLog.Error("json unmarshal fail:", err)
			continue
		}
		radioContext.Stats.Freq = message.Vdl2Phy.Freq
		radioContext.Stats.FreqSkew = message.Vdl2Phy.FreqSkew
		radioContext.Stats.RxBytes += uint64(message.Vdl2Phy.BurstLenOctets)
		radioContext.Stats.SigLevel += message.Vdl2Phy.SigLevel
		radioContext.Stats.SigLevelNum++
		radioContext.Stats.NoiseLevel += message.Vdl2Phy.NoiseLevel
		radioContext.Stats.NoiseLevelNum++
		radioContext.Stats.RxNum++
		logger.AppLog.Infoln("radioContext:", radioContext)
	}
}

var msg = `
{
	"vdl2_phy": {
	  "freq": 136975000,
	  "burst_len_octets": 504,
	  "hdr_bits_fixed": 0,
	  "octets_corrected_by_fec": 0,
	  "idx": 0,
	  "sig_level": -9.840537,
	  "noise_level": 1.799104,
	  "freq_skew": -0.070508
	}
  }
`

func updateRadioStat() {
	updateRadioFrequencyStat()
	updateRadioThroughoutStat()
	updateRadioSignalStat()
	updateRadioMessageStat()
}

func updateRadioFrequencyStat() {
	// var message RadioMessage
	// err := json.Unmarshal([]byte(msg), &message)
	// if err != nil {
	// 	logger.AppLog.Error("fail parse amfRanInfos")
	// 	return
	// }
	// radioContext.Stats.RxBytes += uint64(message.Vdl2.BurstLenOctets)

	dataAttr := &webDataAttr[WEB_DATA_TYPE_RADIO_FREQUENCY]
	st := C.rrdset_find_bytype(dataAttr.Family, dataAttr.Type)
	if st == nil {
		return
	}
	C.rrdset_next(st)
	defer C.rrdset_done(st)

	logger.AppLog.Infoln("Freq:", radioContext.Stats.Freq)
	logger.AppLog.Infoln("FreqSkew:", radioContext.Stats.FreqSkew)

	RrddimFindAndSet(st, "freq", dataAttr, C.longlong(radioContext.Stats.Freq))
	RrddimFindAndSet(st, "freq skew", dataAttr, C.longlong(radioContext.Stats.FreqSkew))
}

func updateRadioThroughoutStat() {
	// var message RadioMessage
	// err := json.Unmarshal([]byte(msg), &message)
	// if err != nil {
	// 	logger.AppLog.Error("fail parse amfRanInfos")
	// 	return
	// }
	// radioContext.Stats.RxBytes += uint64(message.Vdl2.BurstLenOctets)

	dataAttr := &webDataAttr[WEB_DATA_TYPE_RADIO_THROUGHOUT]
	st := C.rrdset_find_bytype(dataAttr.Family, dataAttr.Type)
	if st == nil {
		return
	}
	C.rrdset_next(st)
	defer C.rrdset_done(st)

	logger.AppLog.Infoln("RxBytes:", radioContext.Stats.RxBytes)
	logger.AppLog.Infoln("TxBytes:", radioContext.Stats.TxBytes)

	RrddimFindAndSet(st, "send", dataAttr, C.longlong(radioContext.Stats.TxBytes))
	RrddimFindAndSet(st, "receive", dataAttr, C.longlong(radioContext.Stats.RxBytes))
}

func updateRadioSignalStat() {
	// var message RadioMessage
	// err := json.Unmarshal([]byte(msg), &message)
	// if err != nil {
	// 	logger.AppLog.Error("fail parse amfRanInfos")
	// 	return
	// }
	// radioContext.Stats.SigLevel += message.Vdl2.SigLevel
	// radioContext.Stats.SigLevelNum++
	// radioContext.Stats.NoiseLevel += message.Vdl2.NoiseLevel
	// radioContext.Stats.NoiseLevelNum++

	dataAttr := &webDataAttr[WEB_DATA_TYPE_RADIO_SIGNAL]
	st := C.rrdset_find_bytype(dataAttr.Family, dataAttr.Type)
	if st == nil {
		return
	}
	C.rrdset_next(st)
	defer C.rrdset_done(st)

	if radioContext.Stats.SigLevelNum > 0 {
		avgSigLevel := radioContext.Stats.SigLevel / float64(radioContext.Stats.SigLevelNum)
		logger.AppLog.Infoln("avgSigLevel:", avgSigLevel)
		RrddimFindAndSet(st, "signal level", dataAttr, C.longlong(avgSigLevel))
		radioContext.Stats.SigLevel = 0
		radioContext.Stats.SigLevelNum = 0
	}

	if radioContext.Stats.NoiseLevelNum > 0 {
		avgNoiseLevel := radioContext.Stats.NoiseLevel / float64(radioContext.Stats.NoiseLevelNum)
		logger.AppLog.Infoln("avgNoiseLevel:", avgNoiseLevel)
		RrddimFindAndSet(st, "noise level", dataAttr, C.longlong(avgNoiseLevel))
		radioContext.Stats.NoiseLevel = 0
		radioContext.Stats.NoiseLevelNum = 0
	}
}

func updateRadioMessageStat() {
	// var message RadioMessage
	// err := json.Unmarshal([]byte(msg), &message)
	// if err != nil {
	// 	logger.AppLog.Error("fail parse amfRanInfos")
	// 	return
	// }
	// radioContext.Stats.RxNum++
	// radioContext.Stats.TxNum++

	dataAttr := &webDataAttr[WEB_DATA_TYPE_RADIO_MESSAGE]
	st := C.rrdset_find_bytype(dataAttr.Family, dataAttr.Type)
	if st == nil {
		return
	}
	C.rrdset_next(st)

	logger.AppLog.Infoln("RxNum:", radioContext.Stats.RxNum)
	logger.AppLog.Infoln("TxNum:", radioContext.Stats.TxNum)

	RrddimFindAndSet(st, "send", dataAttr, C.longlong(radioContext.Stats.TxNum))
	RrddimFindAndSet(st, "receive", dataAttr, C.longlong(radioContext.Stats.RxNum))

	C.rrdset_done(st)
}
